import { ScrollView, View } from '@tarojs/components';
import { useEffect, useRef, useState } from 'react';
import { Tabs } from '@nutui/nutui-react-taro';
import { toClassName as to } from '@/utils';
import Empty from '@/components/Empty';
import { useRequest } from 'taro-hooks';
import SelectNode from '../SelectNode';
import styles from './style.scss';
import Taro from '@tarojs/taro';

type Props<T> = {
  fieldNames?: {
    label: keyof T;
    value: keyof T;
    children?: keyof T;
  };
  lazyApi?: (...any) => Promise<T[]>;
  buildParams?: (row?: T) => any;
  options?: any[];
  value?: any[];
  customLeaf?: (row: T) => boolean;
  customDisabled?: (row: T) => boolean;
  customLabel?: (label: string[]) => any;
  changeOnSelect?: boolean;
  onChange?: (ids: string[], options: T[]) => void;
  placeholder?: string;
  open?: boolean;
  hideForm?: boolean;
};

type Item = {
  data: any[];
  title: string;
  value: any;
};

export default <T = any,>(props: Props<T>) => {
  const {
    options,
    fieldNames = {
      label: 'label',
      value: 'value',
      children: 'children',
    },
    lazyApi,
    buildParams,
    customLeaf,
    customDisabled,
    customLabel,
    value,
    changeOnSelect = true,
    onChange,
    placeholder,
    hideForm,
    open = false,
  } = props;
  const [treeData, setTreeData] = useState<Item[]>([]);
  const [visible, setVisible] = useState(false);
  const [current, setCurrent] = useState(0);
  const [name, setName] = useState('');
  const cache = useRef<any>([]);

  const buildOption = (nodes: any[], id?: any): any => {
    const data =
      nodes?.map?.((item) => ({
        ...item,
        value: item[fieldNames.value],
        children: item[fieldNames.children],
        label: item[fieldNames.label],
        isLeaf: customLeaf?.(item),
        disabled: customDisabled?.(item),
      })) || [];
    return {
      title: data.find((item) => item.value === id)?.label,
      data,
      value: id,
    };
  };

  const setData = (index, nodes: any[], id?: any) => {
    const steps = treeData.slice(0, index - 1);
    cache.current = [...steps, buildOption(nodes, id)];
    setTreeData(cache.current);
  };
  const { run } = useRequest(lazyApi!, {
    manual: true,
  });

  const updateName = (names: string[]) => {
    const n = names.filter(Boolean);
    setName(customLabel ? customLabel?.(n) : n.join('/'));
  };

  useEffect(() => {
    let arr: any[] = [];
    const updateStatus = (index, parentNode) => {
      if (index < (value?.length || 0) - 1) {
        loop(index + 1, parentNode || {});
      } else {
        setTreeData((cache.current = arr));
        setCurrent((value?.length || 1) - 1);
        updateName(cache.current.map((item) => item.title));
      }
    };
    const loop = (index, parentNode?: any) => {
      const id = value?.[index];
      const isExist = cache.current?.[index]?.data?.find(
        (item) => item.value === id
      );
      if (lazyApi) {
        if (isExist) {
          arr.push(buildOption(cache.current?.[index]?.data || [], id));
          updateStatus(index, isExist);
        } else {
          if (parentNode?.children?.length) {
            arr.push(buildOption(parentNode?.children || [], id));
            const currentNode = arr?.[index]?.data?.find(
              (item) => item.value === id
            );
            updateStatus(index, currentNode);
          } else {
            run(buildParams?.(parentNode || {})).then((res) => {
              arr.push(buildOption(res || [], id));
              const currentNode = arr?.[index]?.data?.find(
                (item) => item.value === id
              );
              updateStatus(index, currentNode);
            });
          }
        }
      } else {
        if (isExist) {
          arr.push(buildOption(cache.current?.[index]?.data || [], id));
          updateStatus(index, isExist);
        } else {
          let data: any = index === 0 ? options : parentNode?.children;
          arr.push(buildOption(data || [], id));
          const currentNode = arr?.[index]?.data?.find(
            (item) => item.value === id
          );
          updateStatus(index, currentNode);
        }
      }
    };
    loop(0);
  }, [value, options]);
  const submit = () => {
    const empty = !treeData.some((item) => item.value);
    if (empty) return Taro.showToast({ title: '请选择', icon: 'none' });
    const v = treeData.map((item) => item.value).filter(Boolean);
    const opt = treeData
      .filter((i) => v.includes(i.value))
      .map((item) => item.data.find((i) => i.value === item.value));
    onChange?.(v, opt);
    setVisible(false);
  };

  useEffect(() => {
    setVisible(open);
  }, [open]);
  return (
    <View>
      {hideForm || (
        <SelectNode
          onClick={() => setVisible(true)}
          value={name}
          placeholder={placeholder}
        />
      )}
      {visible && (
        <>
          <View className={styles.mask} onClick={() => setVisible(false)} />
          <View className={styles.popupBody}>
            <View className={styles.header}>
              <View className={styles.Button} onClick={() => setVisible(false)}>
                取消
              </View>
              {changeOnSelect && (
                <View className={styles.Button} onClick={submit}>
                  确定
                </View>
              )}
            </View>
            <Tabs value={current} onChange={(e: any) => setCurrent(e)}>
              {treeData?.map((item, index) => (
                <Tabs.TabPane title={item.title || '请选择'} key={index}>
                  <ScrollView scrollY className={styles.cascaderPan}>
                    {item.data?.map((child) => (
                      <View
                        className={to([
                          styles.item,
                          child.disabled && styles.disabled,
                          item.value === child.value && styles.active,
                        ])}
                        key={child.value}
                        onClick={() => {
                          if (child.disabled) return;
                          const { isLeaf, children, label, value: v } = child;
                          item.title = label;
                          item.value = v;
                          // 为子节点或者children长度为0 并且没有配置懒加载api 自动选择节点
                          if (isLeaf || (!children?.length && !lazyApi)) {
                            const d = treeData.slice(0, index + 1);
                            updateName(d.map(({ title }) => title));
                            submit();
                            setTreeData((cache.current = d));
                            setVisible(false);
                          } else {
                            if (children?.length) {
                              setData(index + 2, children);
                              setCurrent(index + 1);
                            } else {
                              run(buildParams?.(child || {})).then((res) => {
                                if (!res?.length) return;
                                setData(index + 2, res);
                                setCurrent(index + 1);
                              });
                            }
                          }
                        }}
                      >
                        {child.label}
                      </View>
                    ))}
                    {!item.data.length && <Empty hideDetail />}
                  </ScrollView>
                </Tabs.TabPane>
              ))}
            </Tabs>
          </View>
        </>
      )}
    </View>
  );
};
